#include "sprite.h"
#include "mapping.h"
#include <stdio.h>

#define LONGTOWORD(val) ((val&0xFFFF0000)>>16)
extern object *objects;

extern signed long v_screenposx;
extern signed long FFFFF708;
extern signed long FFFFF718;
word v_spritecount;

word v_spritetablebuffer[0x200];

word v_spritequeue[0x400];//shall be twice the size for the pointers here

word* getSpriteTableBuf() { return v_spritetablebuffer; }


     
void BuildSprites()   
{
/*
     BldSpr_ScrPos:	dc.l 0			; blank
		dc.l $FFF700		; main screen x-position
		dc.l $FFF708		; background x-position	1
		dc.l $FFF718		; background x-position	2
; ---------------------------------------------------------------------------
; Subroutine to	convert	mappings (etc) to proper Megadrive sprites
; ---------------------------------------------------------------------------

*/
        
        long *BldSpr_ScrPos[] = {0,&v_screenposx,&FFFFF708,&FFFFF718};
  
        //BuildSprites:				; XREF: GM_Title; et al
	//lea	(v_spritetablebuffer).w,a2 ; set address for sprite table
	word *spriteTableBufferPtr = v_spritetablebuffer;
		
        //moveq	#0,d5
	word spriteCount = 0;
		
        //lea	(v_spritequeue).w,a4
	word *spriteQueuePtr = (word*)v_spritequeue;
		
        //moveq	#7,d7
        word priorityCount = 7;
        
        for (;priorityCount>0;priorityCount--)
        {
            //loc_D66A
	    //tst.w	(a4)
	    //beq.w	loc_D72E	    if (spriteQueuePtr[0] != 0)    //if sprite count for this priority level is not 0 (empty)
	    {
                //moveq	#2,d6
                word curSpriteOffset = 1;  //this is 2 in rom for size of word (skip sprite count)

                do                         //build sprite per object in the sprite queue, removing objects from the queue as their sprites are built
                {
                   //loc_D672:
    		   //movea.w	(a4,d6.w),a0
    		   //object *obj=(object*)(spriteQueuePtr[curSpriteOffset+1]+(spriteQueuePtr[curSpriteOffset]<<16));  //current object at this priority
                   object *obj=(object*)(spriteQueuePtr[curSpriteOffset] + (long)objects);               

                   //tst.b	(a0)
    		   //beq.w	loc_D726
    		        
    		   if (obj->ID != 0)  //if this is a valid object
    		   {   
                   
                   //bclr	#7,obRender(a0)
                   obj->Render = obj->Render - (obj->Render & 0x80); //display flag, remove obj from screen while building
                                      
                   //move.b	obRender(a0),d0
                   //move.b	d0,d4
    		   //andi.w	#$C,d0

                   byte oldrender = obj->Render;
    		   byte render = oldrender & 0xC;
    		   signed short obX, obY;
    		      
                   //beq.s	loc_D6DE
    		   if (render == 0)                     //absolute screen positioning...
                   {

                       //loc_D6DE:
                       //move.w	$A(a0),d2
                       obY = obj->ScreenY;		
    
                       //move.w	obX(a0),d3
                       obX = obj->X;
 
                       //loc_D700:
                      BuildSprite(obj, obX, obY, spriteTableBufferPtr, spriteCount, oldrender); 

                   }
                   else                                 //if positioning is not absolute screen coordinates,
    		   {
                      //movea.l	BldSpr_ScrPos(pc,d0.w),a1
                      long *scrPosPtr = BldSpr_ScrPos[render>>2];  //use these positioning attributes
                      
                      //moveq	#0,d0
    		      //move.b	obActWid(a0),d0
    		      byte obActWid=obj->ActionWidth;
                      
                      //move.w	obX(a0),d3
                      //sub.w	(a1),d3 
                      obX = obj->X - LONGTOWORD(*scrPosPtr);      //calculate the proper x position based on the position attributes
                      
                      //move.w	d3,d1
    		      //add.w	d0,d1
    		      signed short d1 = obX+obActWid;   //also include action width
    		          
                      //bmi.w	loc_D726
    		      if (d1 >= 0)              //checks if sonic atleast actionWidth pixels from either left or right edges
                      {                 
                          //move.w	d3,d1
    		          //sub.w	d0,d1
    		          d1=obX-obActWid;
    		                                   
                          //cmpi.w	#$140,d1
    		          //bge.s	loc_D726
    		          if (d1 < 0x140)     //on screen x-wise
    		          {
                              
                              //addi.w	#$80,d3
        		      obX+=0x80;                     //account for the -128 pix offset the vdp sprite system
                              
                              //btst	#4,d4                                                            
                              if ((oldrender & 0x10)==0x10)  //if flag for assume height not set
                              {
        		          //beq.s	loc_D6E8

                                  //moveq	#0,d0
        		          //move.b	obHeight(a0),d0        
        		          byte obHeight = obj->Height;  //use player height
        		                  
                                  //move.w	obY(a0),d2
        		          obY = obj->Y;
        		          
                                  //sub.w	4(a1),d2
                                  obY = obY - LONGTOWORD(*(scrPosPtr+(4/4)));
    		                     
    		                  //add.w	d0,d1
    		                  d1 = d1 + obHeight;

                                  //bmi.s	loc_D726
    		                  if (d1 >= 0)
                                  {
                                         
                                       //move.w	d2,d1
    		                       //sub.w	d0,d1
    		                       d1 = d1 - obHeight;
    		                             
                                       //cmpi.w	#$E0,d1
                                       //bge.s	loc_D726
                                       if (d1 < 0xE0)
                                       {
    		                            //addi.w	#$80,d2
                                            obY = obY + 0x80;    //account for the -128 pix offset the vdp sprite system
                                           
    		                            //bra.s	loc_D700
                                            BuildSprite(obj, obX, obY, spriteTableBufferPtr, spriteCount, oldrender); 
                                       }
                                   }
                              }
                              else
                              {        //assume height flag not set
                                  //loc_D6E8:
    		                  //move.w	obY(a0),d2
                                  //sub.w	obMap(a1),d2         //should not be obMap, should be just 4, for screen position index offset
                                  //addi.w	#$80,d2
    		                  obY = (obj->Y - LONGTOWORD(*(scrPosPtr+(4/4))));
                                  obY = obY + 0x80;
                                  
                                  //cmpi.w	#$60,d2
                                  //blo.s	loc_D726
                                  //cmpi.w	#$180,d2
                                  //bhs.s	loc_D726
    		                  if (obY > 0x60 && obY <= 0x180)
    		                  {
                                      BuildSprite(obj, obX, obY, spriteTableBufferPtr, spriteCount, oldrender); 
                                  }
                              }        
                          }
                      }
                  }
                }
                //loc_D726:
                //addq.w	#2,d6; //d6 =4
    	        curSpriteOffset = curSpriteOffset + 1;                //next object in queue, 1 word (2 words for absolute ptr)
    		
                //subq.w	#2,(a4)
    	        spriteQueuePtr[0] = spriteQueuePtr[0] - 1;        //subtract 1 from sprite count, this is 2 in ROM

            } while (spriteQueuePtr[0] != 0);
            //bne.w	loc_D672
              
       }
       //loc_D72E
       //lea	$80(a4),a
       spriteQueuePtr = &spriteQueuePtr[/*0x80*/0x40];       //next priority level

       //dbf	d7,loc_D66A
   }
   
   //move.b	d5,(v_spritecount).w
   v_spritecount = spriteCount;
        
   //cmpi.b	#$50,d5
   //beq.s	loc_D748
   if (spriteCount!=0x50)
   {
      //move.l	#0,(a2)
      //rts
      *(spriteTableBufferPtr)=0;
      *(spriteTableBufferPtr+1)=0;
   }
   else
   {
       //loc_D748
       //loc_D748
       //move.b	#0,-5(a2)
	
       *(spriteTableBufferPtr-3)=(*spriteTableBufferPtr-3)&0xFF00;

       //rts
   }
 	
//; ===========================================================================





//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

} //; End of function BuildSprites

void BuildSprite(object *obj, signed short obX, signed short obY, word* &spriteTableBufferPtr, word &spriteCount, byte oldrender)
{
    // bra.s	loc_D700
    //; ===========================================================================  
                                               
    //movea.l	obMap(a0),a1
    //word *obMapPtr=obj->Map;
    //moveq	#0,d1                       
    //btst	#5,d4
    if ((oldrender&0x20)==0x20)    //raw mappings flag, if bit set only 5 bytes read from mappings 
    {
        //bne.s	loc_D71C
    }
	            
    // MAPPINGS FORMAT   =   http://sonicology.fateback.com/hacks/mappings/sonic1_format.htm
    //     NN - ( YY - SS - FF - TT - XX )
    //   NN = number of mappings in this group
    //   each mapping in this group has format specified between () above...
    //   YY = The position of the sprite piece on the y-axis. YY increases as you move an item towards the bottom of the screen.
    //   SS = The size and shape of the sprite piece. 8x8 tiles are read from the VRAM and placed into differently-shaped rectangles:
    //      size/shape is never larger then 4 by 4 tiles
    //   FF = This byte controls the mirroring and flipping of the sprite piece, and also which pallete line the sprite piece uses.
    //   TT = This controls which tiles in the VRAM are to be read. Normally, 00 is the first tile of the sprite you're dealing with.
    //   XX = The position of the sprite piece on the x-axis. XX increases as you move an item towards the right-hand-side of the screen.
                        
    //move.b obFrame(a0),d1
    //add.b	d1,d1
    byte frame=obj->Frame;       //this is *2 in rom for size of frame 
    		
    //adda.w	(a1,d1.w),a1
    byte *obMapPtr=obj->Map[frame];
 
    //move.b	(a1)+,d1
    //subq.b	#1,d1
    signed char mapCount=(*(obMapPtr++)-1);
                        
    //bmi.s	loc_D720
    if (mapCount >= 0)           //mapping count is positive...
    {
    //loc_D71C:
    //bsr.w	sub_D750
    
    //-------------sub_D750--------------				
    
        //; XREF: BuildSprites
        //movea.w	obGfx(a0),a3
        word obGfx = obj->Gfx;
                           
        //btst	#0,d4
        if ((oldrender&1)==1)                   //horizontal mirror flag
        { 
            //bne.s	loc_D796
            if ((oldrender&2)!=2)               //vertical mirror flag
            {
                for (; mapCount>=0; mapCount--)
                {
                    //loc_D79E 
                    if (spriteCount==0x50) { asm volatile("leave"); return; }                 //m
                                           
                    signed char blockY=*(obMapPtr++);
  
                    signed short blockYWord2=blockY;
                    signed short blockYWord=blockYWord2+obY;                       //add to y position of player to get position onscreen
                    (*spriteTableBufferPtr++)=blockYWord;            //move to sprite table buffer, grabs directly from here when display sprite onscreen
                                       
                    byte blockSize=*(obMapPtr++);     

                    spriteCount=spriteCount+1;                       //mappings count?
                    (*spriteTableBufferPtr++)=blockSize+(spriteCount<<8);     //moving 2 bytes, blocksize and count
                                       
                    word blockTileLoc=((*obMapPtr++)<<8);
                    blockTileLoc+=(*obMapPtr++);
                    word blockVRam=(blockTileLoc+obGfx)^0x800;
                                       
                    (*spriteTableBufferPtr++)=blockVRam;
                                       
                    signed char blockX=-*(obMapPtr++);         //negated this time
                    
                    signed short blockOffset = ((blockSize * 2) & 0x18) + 8;
                    signed short blockXLeft=blockX - blockOffset;
                    signed short blockXWord=(blockXLeft+obX)&0x1FF;   
                    if (blockXWord==0) { blockXWord=1; }
                    (*spriteTableBufferPtr++)=blockXWord;
                }
            }
            else
            {
                for (; mapCount>=0; mapCount--)
                {
                    //loc_D82A
                    if (spriteCount==0x50) { asm volatile("leave"); return; }
                                       
                    signed char blockY = -*(obMapPtr++);
                    byte blockSize=*(obMapPtr++);                    

                    signed short blockYOffset = ((blockSize * 8) & 0x18) + 8;
                    signed short blockYTop=blockY - blockYOffset;                       //add to y position of player to get position onscreen
                    signed short blockYWord = blockYTop + obY;
                    (*spriteTableBufferPtr++)=blockYWord;            //move to sprite table buffer, grabs directly from here when display sprite onscreen
                                       
                    spriteCount=spriteCount+1;                       //mappings count?
                    (*spriteTableBufferPtr++)=blockSize+(spriteCount<<8);     //moving 2 bytes, blocksize and count
                                       
		    word blockTileLoc=((*obMapPtr++)<<8);
		    blockTileLoc+=(*obMapPtr++);
		    word blockVRam=(blockTileLoc+obGfx)^0x1800;
                                       
                    (*spriteTableBufferPtr++)=blockVRam;
                                       
                    signed char blockX=-*(obMapPtr++);         //negated this time

                    signed short blockOffset = ((blockSize * 2) & 0x18) + 8;
                    signed short blockXLeft=blockX - blockOffset;
                    signed short blockXWord=(blockXLeft+obX)&0x1FF;    
                    if (blockXWord==0) { blockXWord=1; }
                    (*spriteTableBufferPtr++)=blockXWord;
                }
            }
        }
        else if ((oldrender&2)==2)
        {
                           
            //btst	#1,d4
    	    //bne.w	loc_D7E4
            for (; mapCount>=0; mapCount--)
            {
                if (spriteCount==0x50) { asm volatile("leave"); return; }
                                           
                signed char blockY = -*(obMapPtr++);
                byte blockSize=*(obMapPtr++);                    

                signed short blockYOffset = ((blockSize * 8) & 0x18) + 8;
                signed short blockYTop=blockY - blockYOffset;                       //add to y position of player to get position onscreen
                signed short blockYWord = blockYTop + obY;                
                (*spriteTableBufferPtr++)=blockYWord;            //move to sprite table buffer, grabs directly from here when display sprite onscreen
                                   
                spriteCount=spriteCount+1;                       //mappings count?
                (*spriteTableBufferPtr++)=blockSize+(spriteCount<<8);     //moving 2 bytes, blocksize and count
                                   
                word blockTileLoc=((*obMapPtr++)<<8);
                blockTileLoc+=(*obMapPtr++);
                word blockVRam=(blockTileLoc+obGfx)^0x1000;
                                   
                (*spriteTableBufferPtr++)=blockVRam;
                                   
                signed char blockX=*(obMapPtr++);
                signed short blockXWord=blockX;
                blockXWord=(blockXWord+obX)&0x1FF; 
                if (blockXWord==0) { blockXWord=1; }
                (*spriteTableBufferPtr++)=blockXWord;
            }
        }
        else
        {
            //sub_D762
            for (; mapCount>=0; mapCount--)
            {
                if (spriteCount==0x50) { asm volatile("leave"); return; }
                                       
                signed char blockY=*(obMapPtr++);
                signed short blockYWord=blockY;
                blockYWord=blockYWord+obY;                                      //add to y position of player to get position onscreen
                (*spriteTableBufferPtr++)=blockYWord;      //move to sprite table buffer, grabs directly from here when display sprite onscreen
                                   
                byte blockSize=*(obMapPtr++);                    //
                spriteCount=spriteCount+1;                       //mappings count?
                (*spriteTableBufferPtr++)=blockSize+(spriteCount<<8);     //moving 2 bytes, blocksize and count
                                   
                word blockTileLoc=((*obMapPtr++)<<8);
                blockTileLoc+=(*obMapPtr++);
                word blockVRam=blockTileLoc+obGfx;
                                   
                (*spriteTableBufferPtr++)=blockVRam;
                                   
                signed char blockX=*(obMapPtr++);
                signed short blockXWord=blockX;
                blockXWord=(blockXWord+obX)&0x1FF;   
                if (blockXWord==0) { blockXWord=1; }
                (*spriteTableBufferPtr++)=blockXWord;
            }
        }   
    }

    //------------- End of sub_D750 -------------//

    //loc_D720:
    //bset	#7,obRender(a0)                   
    obj->Render=obj->Render|0x80;   //the onscreen flag is reset
    
}

void DisplaySprite(object *obj)
{
//; ---------------------------------------------------------------------------
//; Subroutine to	display	a sprite/object, when a0 is the	object RAM
//; ---------------------------------------------------------------------------
//; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

//DisplaySprite:
//		lea	(v_spritequeue).w,a1
        word *spriteQueuePtr=v_spritequeue;
    
        //      move.w	obPriority(a0),d0 ; get sprite priority
	//	lsr.w	#1,d0
	//	andi.w	#$380,d0
	word priority=(((((obj->Priority)<<8)+(obj->ActionWidth))/2)&0x380)/2;
	
        
	//printf("DisplaySprite()\n");
	//printf("priority: %i\n", priority);
	
	//adda.w	d0,a1		; jump to position in queue
	word *spriteListPtr=&spriteQueuePtr[priority];
	//printf("spriteListPtr: %i\n", spriteListPtr);
	
    //	cmpi.w	#$7E,(a1)	; is this part of the queue full?
	if (*spriteListPtr < (0x7E/2))
	{
    	    //bcc.s	DSpr_Full	; if yes, branch
	    *spriteListPtr = *spriteListPtr + 1;  //this is 2 in rom...
		
            //addq.w	#2,(a1)		; increment sprite count
	    //adda.w	(a1),a1		; jump to empty position
	    word *endListPtr=(spriteListPtr+(*spriteListPtr));
		
            //move.w	a0,(a1)		; insert RAM address for object
        
            long objaddress=(long)(obj);
            long objlistaddress=(long)(objects);
            word relOffset=objaddress - objlistaddress;

            /* *(endListPtr+1)=(objaddress)&0x0000FFFF; 
               *(endListPtr)=((objaddress)&0xFFFF0000)>>16; */
            
            *(endListPtr) = relOffset;
    }
	//DSpr_Full:
	//	rts	

    //; End of function DisplaySprite
}

/*
; ---------------------------------------------------------------------------
; Subroutine to	display	a 2nd sprite/object, when a1 is	the object RAM
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||


DisplaySprite1:
		lea	(v_spritequeue).w,a2
		move.w	obPriority(a1),d0
		lsr.w	#1,d0
		andi.w	#$380,d0
		adda.w	d0,a2
		cmpi.w	#$7E,(a2)
		bcc.s	DSpr1_Full
		addq.w	#2,(a2)
		adda.w	(a2),a2
		move.w	a1,(a2)

	DSpr1_Full:
		rts	

; End of function DisplaySprite1
}*/

void AnimateSprite(object *obj, byte *AniScriptList[])
{
    //AnimateSprite:
    //moveq	#0,d0
    //move.b	obAnim(a0),d0	; move animation number	to d0
    byte obAnim = obj->Anim;

    //cmp.b	obNextAni(a0),d0 ; is animation set to restart?
    //beq.s	Anim_Run	; if not, branch
    if (obAnim != obj->NextAni)
    {
        //move.b	d0,obNextAni(a0) ; set to "no restart"
        obj->NextAni = obAnim;

	//move.b	#0,obAniFrame(a0) ; reset animation
	//move.b	#0,obTimeFrame(a0) ; reset frame duration
        obj->AniFrame = 0;
        obj->TimeFrame = 0;
    }

    //Anim_Run:
    //subq.b	#1,obTimeFrame(a0) ; subtract 1 from frame duration
    obj->TimeFrame = obj->TimeFrame - 1;

    //bpl.s	Anim_Wait	; if time remains, branch
    if (obj->TimeFrame < 0)
    {
        //add.w	d0,d0
        //adda.w	(a1,d0.w),a1	; jump to appropriate animation	script
        byte *aniScript = AniScriptList[obj->Anim];

	//move.b	(a1),obTimeFrame(a0) ; load frame duration
        obj->TimeFrame = aniScript[0];

	//moveq	#0,d1
	//move.b	obAniFrame(a0),d1 ; load current frame number
        byte curFrame = obj->AniFrame;

	//move.b	1(a1,d1.w),d0	; read sprite number from script
        byte curSprite = aniScript[1+curFrame];

	//bmi.s	Anim_End_FF	; if animation is complete, branch
        if (curSprite >= 0x80)
        {
            //; ===========================================================================
            //Anim_End_FF:
	    //addq.b	#1,d0		; is the end flag = $FF	?
            //bne.s	Anim_End_FE	; if not, branch
            if (curSprite == 0xFF)
            {
		//move.b	#0,obAniFrame(a0) ; restart the animation
		obj->AniFrame = 0;

                //move.b	1(a1),d0	; read sprite number
		curSprite = aniScript[1];

                //bra.s	Anim_Next
            }
            else if (curSprite == 0xFE)
            {
                //; ===========================================================================

                //Anim_End_FE:
		//addq.b	#1,d0		; is the end flag = $FE	?
		//bne.s	Anim_End_FD	; if not, branch
		//move.b	2(a1,d1.w),d0	; read the next	byte in	the script
		byte jumpBack = aniScript[2+curFrame];
                
                //sub.b	d0,obAniFrame(a0) ; jump back d0 bytes in the script
                obj->AniFrame = obj->AniFrame - jumpBack;
        
		//sub.b	d0,d1
                curFrame = curFrame - jumpBack;
 
		//move.b	1(a1,d1.w),d0	; read sprite number
		curSprite=aniScript[1+curFrame];
                
                //bra.s	Anim_Next
            }
            else
            { 
                if (curSprite == 0xFD)
                {
                    //Anim_End_FD:
		    //addq.b	#1,d0		; is the end flag = $FD	?
		    //bne.s	Anim_End_FC	; if not, branch
		    //move.b	2(a1,d1.w),obAnim(a0) ; read next byte, run that animation
                    obj->Anim = aniScript[2+curFrame];
                }
                else if (curSprite == 0xFC)
                {
                    //Anim_End_FC:
		    //addq.b	#1,d0		; is the end flag = $FC	?
		    //bne.s	Anim_End_FB	; if not, branch
		    //addq.b	#2,obRoutine(a0) ; jump to next routine
                    obj->Routine = obj->Routine + 2;
                }
                else if (curSprite == 0xFB)
                {
                    //Anim_End_FB:
		    //addq.b	#1,d0		; is the end flag = $FB	?
		    //bne.s	Anim_End_FA	; if not, branch
		    //move.b	#0,obAniFrame(a0) ; reset animation
		    obj->AniFrame = 0;

                    //clr.b	ob2ndRout(a0)	; reset	2nd routine counter
                    obj->secondRoutine = 0;
                }
                else if (curSprite == 0xFA)
                {
                    //Anim_End_FA:
		    //addq.b	#1,d0		; is the end flag = $FA	?
		    //bne.s	Anim_End	; if not, branch
		    //addq.b	#2,ob2ndRout(a0) ; jump to next routine
                    obj->secondRoutine = obj->secondRoutine + 2;
                }
                
                //Anim_End:
		//rts
                return;
            }
        }
	
                
        //Anim_Next:
	//move.b	d0,d1
	//andi.b	#$1F,d0
        byte newFrame = curSprite & 0x1F;

	//move.b	d0,obFrame(a0)	; load sprite number
        obj->Frame = newFrame;

	//move.b	obStatus(a0),d0
        byte status = obj->Status;

	//rol.b	#3,d1
        //eor.b	d0,d1
	//andi.b	#3,d1
        byte renderFlags = ( ((curSprite << 3) | (curSprite >> 5)) ^ status) & 3;
	    
        //andi.b	#$FC,obRender(a0)
        obj->Render = obj->Render & 0xFC;

	//or.b	d1,obRender(a0)
        obj->Render = obj->Render | renderFlags;

	//addq.b	#1,obAniFrame(a0) ; next frame number
        obj->AniFrame = obj->AniFrame + 1;
        
    }   
    
    //Anim_Wait:
    //rts
}   //; End of function AnimateSprite
